#ifdef ENABLE_MAPLE_SAN

#ifndef MAPLE_SAN_INCLUDE_SAN_COMMON_H
#define MAPLE_SAN_INCLUDE_SAN_COMMON_H
#include "me_function.h"
#include "me_phase_manager.h"
#include "mir_builder.h"
#include "mir_function.h"
#include "mir_module.h"
#include "mir_nodes.h"
#include "types_def.h"
#include <map>

namespace maple {

#if TARGX86_64 || TARGAARCH64
#define LOWERED_PTR_TYPE PTY_a64
constexpr uint8 kSizeOfPtr = 8;
#elif TARGX86 || TARGARM32 || TARGVM
#define LOWERED_PTR_TYPE PTY_a32
constexpr uint8 kSizeOfPtr = 4;
#else
#error "Unsupported target"
#endif

typedef std::pair<StmtNode*, BlockNode*> StmtBlockNodePair;
std::vector<StmtBlockNodePair> getAllOrderedStmtNodeList(BlockNode* block);

struct ASanStackVariableDescription {
  std::string Name;     // Name of the variable that will be displayed by asan
  size_t Size;          // Size of the variable in bytes.
  size_t LifetimeSize;  // Size in bytes to use for lifetime analysis check.
  size_t Alignment;     // Alignment of the variable (power of 2).
  MIRSymbol *Symbol;    // The actual AllocaInst.
  StmtNode *AllocaInst;
  size_t Offset;  // Offset from the beginning of the frame;
  unsigned Line;  // Line number.

  bool operator<(const ASanStackVariableDescription &rhs) const {
    return this->Line > rhs.Line;
  }
};

struct ASanDynaVariableDescription {
  std::string Name;  // Name of the variable that will be displayed by asan
  BaseNode *Size;    // Size of the variable in bytes.
  StmtNode *AllocaInst;
  size_t Offset;  // Offset from the beginning of the frame;
  unsigned Line;  // Line number.

  bool operator<(const ASanDynaVariableDescription &rhs) const {
    return this->Line > rhs.Line;
  }
};

// Output data struct for ComputeASanStackFrameLayout.
struct ASanStackFrameLayout {
  size_t Granularity;     // Shadow granularity.
  size_t FrameAlignment;  // Alignment for the entire frame.
  size_t FrameSize;       // Size of the frame in bytes.
};

class PreAnalysis : public AnalysisResult {
 public:
  PreAnalysis(MemPool &memPoolParam) : AnalysisResult(&memPoolParam){};

  ~PreAnalysis() override = default;

  std::vector<MIRSymbol *> usedInAddrof;
};

typedef uint64_t AsanRazorCheckID;
typedef uint64_t AsanRazorCheckCount;
typedef uint32_t StmtID;

struct AsanVirtualBlock {
  StmtNode* first;
  StmtNode* last;
  // for sanrazor, call coverage interface before crash
  // sometimes, a check has multiple crash branches,
  // we need to insert sanrazor interface for all of them
  std::vector<StmtNode*> crashes;
  StmtNode* normal; // for sanrazor, normal branch is just before normal
};

// this is the id version for use between phases
struct AsanVirtualIDBlock {
  uint32 first;
  uint32 last;
  // for sanrazor, call coverage interface before crash
  // sometimes, a check has multiple crash branches,
  // we need to insert sanrazor interface for all of them
  std::vector<uint32> crashes;
  uint32 normal; // for sanrazor, normal branch is just before normal
};

class AsanVirtualBlockList {
public:
  MapleVector<AsanVirtualBlock>* blocks;
  MapleVector<AsanVirtualIDBlock>* idblocks;

  AsanVirtualBlockList(MIRModule &m) {
    blocks = m.GetMemPool()->New<MapleVector<AsanVirtualBlock>>(m.GetMPAllocator().Adapter());
    idblocks = m.GetMemPool()->New<MapleVector<AsanVirtualIDBlock>>(m.GetMPAllocator().Adapter());
  }
  ~AsanVirtualBlockList() {
    blocks->clear();
    idblocks->clear();
  }
  inline AsanVirtualIDBlock GetAsanIDBlock(const AsanVirtualBlock &block) {
    AsanVirtualIDBlock ret;
    ret.first = block.first->GetStmtID();
    ret.last = block.last->GetStmtID();
    ret.normal = block.normal->GetStmtID();
    for (auto crash : block.crashes) {
      ret.crashes.push_back(crash->GetStmtID());
    }
    return ret;
  }
  void push_back(const AsanVirtualBlock &block) {
    blocks->push_back(block);
    idblocks->push_back(GetAsanIDBlock(block));
  }
};

// Initialize the list pointer while processing 
extern AsanVirtualBlockList* AsanBlockListPtr;

class ModuleAddressSanitizer;
extern ModuleAddressSanitizer* AsanModulePtr;

static const size_t kMinAlignment = 16;
static const unsigned kAllocaRzSize = 32;
static const size_t kNumberOfAccessSizes = 5;

static const uintptr_t kCurrentStackFrameMagic = 0x41B58AB3;
static const uintptr_t kRetiredStackFrameMagic = 0x45E0360E;
static const size_t kMinStackMallocSize = 1 << 6;   // 64B
static const size_t kMaxStackMallocSize = 1 << 16;  // 64K
static const int kMaxAsanStackMallocSizeClass = 10;

// These magic constants should be the same as in
// in asan_internal.h from ASan runtime in compiler-rt.
static const int kAsanStackLeftRedzoneMagic = 0xf1;
static const int kAsanStackMidRedzoneMagic = 0xf2;
static const int kAsanStackRightRedzoneMagic = 0xf3;
static const int kAsanStackUseAfterScopeMagic = 0xf8;

bool isTypeSized(MIRType *type);

int computeRedZoneField(MIRType *type);

size_t TypeSizeToSizeIndex(uint32_t TypeSize);

std::vector<MIRSymbol *> GetGlobalVaribles(const MIRModule &mirModule);

void appendToGlobalCtors(const MIRModule &mirModule, const MIRFunction *func);

void appendToGlobalDtors(const MIRModule &mirModule, const MIRFunction *func);

MIRFunction *getOrInsertFunction(MIRBuilder *mirBuilder, const char *name, MIRType *retType,
                                 std::vector<MIRType *> argTypes);

std::vector<uint8_t> GetShadowBytes(const std::vector<ASanStackVariableDescription> &Vars,
                                    const ASanStackFrameLayout &Layout);

MIRAddrofConst *createSourceLocConst(MIRModule &mirModule, MIRSymbol *Var, PrimType primType);

MIRAddrofConst *createAddrofConst(const MIRModule &mirModule, const MIRSymbol *mirSymbol, PrimType primType);

MIRStrConst *createStringConst(const MIRModule &mirModule, const std::basic_string<char>& Str, PrimType primType);

std::string ComputeASanStackFrameDescription(const std::vector<ASanStackVariableDescription> &vars);

std::vector<uint8_t> GetShadowBytesAfterScope(const std::vector<ASanStackVariableDescription> &Vars,
                                              const ASanStackFrameLayout &Layout);

MIRSymbol *getOrCreateSymbol(MIRBuilder *mirBuilder, const TyIdx tyIdx, const std::string &name, MIRSymKind mClass,
                             MIRStorageClass sClass, MIRFunction *func, uint8 scpID);

ASanStackFrameLayout ComputeASanStackFrameLayout(std::vector<ASanStackVariableDescription> &Vars, size_t Granularity,
                                                 size_t MinHeaderSize);

CallNode* CreateStackCheck(MIRBuilder *mirBuilder, BaseNode *baseNode, BaseNode *sizeNode);
// Start of Sanrazor
int SANRAZOR_MODE();

}  // end namespace maple
#endif  // MAPLE_SAN_INCLUDE_SAN_COMMON_H

#endif